﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using YAPL.Util;
using YAPL.Language.Core;
using YAPL.Language.Core.Types;

namespace YAPL.Language.Compiler.CodeGen.ReflectionEmitGenerator.AstGen.Helpers {
	/// <summary>
	/// Helper for emitting GCL function calls
	/// </summary>
	public static class ILGeneratorGCLExtensions {
		/// <summary>
		/// Emits code which gets specific function from GCL. Instance will be stored on top of evaluation stack.
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		/// <param name="name">function name</param>
		/// <param name="parent">function parent class</param>
		public static void EmitGetFunctionFromGCL(this ILGenerator ilGen, string name, string parent) {
			var methodsMethod = typeof(YAPLClass).GetProperty("Methods").GetGetMethod();
			var getItemMethod = typeof(FunctionCollection).GetIndexerGetMethod(new[] { typeof(String) });

			EmitGetClassFromGCL(ilGen, parent);
			ilGen.Emit(OpCodes.Callvirt, methodsMethod);
			ilGen.Emit(OpCodes.Ldstr, name);
			ilGen.Emit(OpCodes.Callvirt, getItemMethod);
		}

		/// <summary>
		/// Emits code which gets specific function from GCL, when class is not specified at compile time. Instance will be stored on top of evaluation stack.
		/// Requires class name on top of evaluation stack.
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		/// <param name="name">function name</param>
		public static void EmitGetFunctionFromGCLDynamically(this ILGenerator ilGen, string name) {
			var methodsMethod = typeof(YAPLClass).GetProperty("Methods").GetGetMethod();
			var getItemMethod = typeof(FunctionCollection).GetIndexerGetMethod(new[] { typeof(String) });

			ilGen.Emit(OpCodes.Callvirt, methodsMethod);
			ilGen.Emit(OpCodes.Ldstr, name);
			ilGen.Emit(OpCodes.Callvirt, getItemMethod);
		}

		/// <summary>
		/// Emits code which gets specific class from GCL. Instance will be stored on top of evaluation stack.
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		/// <param name="className">class name to get</param>
		public static void EmitGetClassFromGCL(this ILGenerator ilGen, string className) {
			var getFromGCLMethod = typeof(GCL).GetMethod("Get", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(String) }, null);
			ilGen.Emit(OpCodes.Ldstr, className);
			ilGen.Emit(OpCodes.Call, getFromGCLMethod);
		}

		/// <summary>
		/// Emits code which class GCL 'DefineClass' method
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		/// <param name="className">class name</param>
		public static void EmitDefineClass(this ILGenerator ilGen, string className) {
			var defineClassMethod = typeof (GCL).GetMethod("DefineClass", BindingFlags.Static | BindingFlags.Public, null, new[] {typeof (string)}, null);
			ilGen.Emit(OpCodes.Ldstr, className);
			ilGen.Emit(OpCodes.Call, defineClassMethod);
		}
	}
}
